import os
import json
from datetime import datetime, timedelta
from collections import defaultdict
from pyecharts import options as opts
from pyecharts.charts import Line
from pyecharts.globals import ThemeType
import time
import json
import sys
import os
from datetime import datetime
from datetime import datetime, timedelta,time as dtime
from pathlib import Path
import logging
import shutil
from logging.handlers import TimedRotatingFileHandler
import requests

def clear_directory(dir_path):
    """
    清空指定目录的所有内容（保留目录本身）
    
    参数:
        dir_path (str): 要清空的目录路径
    """
    # 确保目录存在
    if not os.path.exists(dir_path):
        os.makedirs(dir_path)
        return
    
    # 验证路径是目录
    if not os.path.isdir(dir_path):
        raise ValueError(f"路径 '{dir_path}' 不是目录")
    
    # 遍历删除目录内容
    for item in os.listdir(dir_path):
        item_path = os.path.join(dir_path, item)
        
        try:
            if os.path.isfile(item_path) or os.path.islink(item_path):
                os.unlink(item_path)  # 删除文件或符号链接
            elif os.path.isdir(item_path):
                shutil.rmtree(item_path)  # 递归删除子目录
        except Exception as e:
            print(f"删除 {item_path} 失败: {str(e)}")
            # 可根据需要添加错误处理逻辑

def setup_logging(
        name: str = "app",
        log_dir: str = "logs",
        level: str = "INFO",
        when: str = "D",
        backup_count: int = 7,
        fmt: str = "%(asctime)s - %(levelname)s - %(module)s:%(lineno)d - %(message)s"
) -> logging.Logger:
    """
    初始化日志配置

    参数：
        name (str): 日志名称/应用名称，默认 'app'
        log_dir (str): 日志存储目录，默认 'logs'
        level (str): 日志级别（DEBUG/INFO/WARNING/ERROR/CRITICAL），默认 'INFO'
        when (str): 日志轮换间隔单位，默认 'D'（天），可选值：
                    'S' 秒, 'M' 分, 'H' 小时, 'D' 天, 'W0'-'W6' 周
        backup_count (int): 保留的旧日志文件数量，默认7
        fmt (str): 日志格式，默认包含时间、级别、模块、行号和消息

    返回：
        logging.Logger: 配置好的Logger对象

    示例：
        >>> logger = setup_logging()
        >>> logger.info("系统启动")
    """
    try:
        # 创建日志目录
        os.makedirs(log_dir, exist_ok=True)

        # 初始化日志对象
        logger = logging.getLogger(name)
        logger.setLevel(level)

        # 清除已有处理器避免重复
        if logger.handlers:
            for handler in logger.handlers[:]:
                logger.removeHandler(handler)

        # 设置日志格式
        formatter = logging.Formatter(fmt=fmt, datefmt="%Y-%m-%d %H:%M:%S")

        # 文件处理器（按时间轮换）
        file_handler = TimedRotatingFileHandler(
            filename=os.path.join(log_dir, f"{name}.log"),
            when=when,
            backupCount=backup_count,
            encoding="utf-8"
        )
        file_handler.setFormatter(formatter)
        logger.addHandler(file_handler)

        # 控制台处理器
        console_handler = logging.StreamHandler(sys.stdout)
        console_handler.setFormatter(formatter)
        logger.addHandler(console_handler)

        return logger

    except PermissionError as e:
        sys.stderr.write(f"日志文件写入权限不足: {str(e)}\n")
        raise
    except Exception as e:
        sys.stderr.write(f"日志初始化失败: {str(e)}\n")
        raise



# 计算总页数
def calculate_total_pages(total_records, count_per_page):
    return (total_records + count_per_page - 1) // count_per_page


#获取请求头
def get_headers(cookie_value):
    return  {
    "Accept": "application/json, text/plain, */*",
    "Accept-Encoding": "gzip, deflate, br",
    "Accept-Language": "zh-CN,zh;q=0.9",
    "Connection": "keep-alive",
    "Cookie": cookie_value,
    "Host": ips,
    "Referer": f"{hosts}/member/history",
    "sec-ch-ua": '"Chromium";v="104", " Not A;Brand";v="99", "Google Chrome";v="104"',
    "sec-ch-ua-mobile": "?0",
    "sec-ch-ua-platform": '"Windows"',
    "Sec-Fetch-Dest": "empty",
    "Sec-Fetch-Mode": "cors",
    "Sec-Fetch-Site": "same-origin",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36"
}
# 文件介绍：定义读取和合并JSON文件的函数

def calculate_event_count():
    # 获取当前时间
    now = datetime.now()
    
    # 计算从00:00:00到当前时间的总秒数
    midnight = now.replace(hour=0, minute=0, second=0, microsecond=0)
    seconds_since_midnight = (now - midnight).total_seconds()
    T = seconds_since_midnight  # 当前时间距离00:00:00的秒数

    # 定义关键时间点（单位：秒）
    A_end = 5 * 3600 + 59 * 60  # 05:59:00 = 21540秒
    C_start = 7 * 3600 + 30 * 60  # 07:30:00 = 27000秒

    # 计算A段固定次数（00:00:00到05:59:00）
    a_count = (A_end // 75)  # 21540 // 75 = 287
    total_count = a_count  # 初始化为A段次数

    # 根据当前时间段计算总次数
    if T < A_end:
        # 在A段：00:00:00 到 05:59:00
        total_count = T // 75  # 直接计算当前秒数包含的次数
    elif T < C_start:
        # 在B段：05:59:00 到 07:30:00（不产生新数字）
        # 保持A段次数不变
        pass
    else:
        # 在C段：07:30:00 之后
        # A段次数 + C段时间内产生的次数
        c_seconds = T - C_start  # C段持续时间
        total_count = a_count + (c_seconds // 75) + 1  # +1包含07:30:00的首次产生

    return int(total_count) + 1  # +1 包含00:00:00的首次产生

def merge_json_files(input_dir, output_file):
    # 初始化一个空列表，用于存储所有的 items
    all_items = []
    path = Path(output_file)
    if path.exists():
        try:
            path.unlink()  # 删除文件
        except Exception as e: 
            logger.error(f"删除文件时出错: {e}")
    else:
        logger.info(f"文件 {output_file} 不存在")

    # 遍历输入目录中的所有文件
    for filename in os.listdir(input_dir):
        if filename.endswith('.json'):
            file_path = os.path.join(input_dir, filename)

            try:
                with open(file_path, 'r', encoding='utf-8') as f:
                    data = json.load(f)

                    # 提取 items 数组并添加到 all_items 列表中
                    items = data.get("items", [])
                    all_items.extend(items)

                print(f"Processed {filename}")

            except json.JSONDecodeError as e:
                print(f"Error decoding JSON in {filename}: {e}")
            except Exception as e:
                print(f"Error processing {filename}: {e}")

    # 将所有 items 合并成一个新的 JSON 文件
    output_data = {
        "items": all_items
    }

    with open(output_file, 'w', encoding='utf-8') as f:
        json.dump(output_data, f, ensure_ascii=False, indent=4)

    return output_file


# 发送GET请求并保存结果
def fetch_and_save_data(cookie_value, total_records, count_per_page, lottery_id,save_dir,TARGET_NUMBERS,BET_CONFIG,COUNTER_DIVISOR,STRATEGY_CONFIG):
    global next_spider_date
    # 计算总页数
    total_pages = calculate_total_pages(total_records, count_per_page)
    print(f"Total pages to fetch: {total_pages}")
    if not os.path.exists(save_dir):
        os.makedirs(save_dir)
    output_filename = f"merged_{datetime.now().strftime('%Y%m%d')}.json"
    output_file = os.path.join(save_dir, output_filename)
    # 循环请求每一页的数据
    for page in range(0, total_pages + 1):
        url = f"{hosts}/api/mem/lottery/history/{lottery_id}?count={count_per_page}&page={page}&lottery_id={lottery_id}"
        headers = get_headers(cookie_value)

        try:
            response = requests.get(url, headers=headers)

            # 检查响应状态码
            if response.status_code == 200:
                data = response.json()

                # 打印返回的结果
                print(f"Fetched page {page}/{total_pages}")

                # 生成文件名
                timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
                filename = f"{save_dir}/{timestamp}_lottery_id_{lottery_id}.json"

                # 保存数据到文件
                with open(filename, 'w', encoding='utf-8') as f:
                    json.dump(data, f, ensure_ascii=False, indent=4)

                print(f"Saved data to {filename}")
            else:
                print(f"Request failed with status code: {response.status_code}")
                print(f"Response: {response.text}")

            # 每次请求后休息2秒
            if page < total_pages:
                time.sleep(2)
        except requests.exceptions.RequestException as e:
            print(f"Request error on page {page}: {e}")
    #合并数据
    merge_json_files(save_dir, output_file)
    # 定义最新一期的开奖时间，便于下次爬取数据的时间
    sorted_items = read_and_sort_data(output_file)
    current_date = sorted_items[-1]["open_time"]
    # 下次爬取数据的时间
    original_date = datetime.strptime(current_date, "%Y-%m-%d %H:%M:%S")
    # 添加 1分25秒
    delta = timedelta(minutes=1, seconds=27)
    next_spider_date = original_date + delta

    calc(output_file,save_dir,TARGET_NUMBERS,BET_CONFIG,COUNTER_DIVISOR,STRATEGY_CONFIG)

def should_execute():
    """判断当前时间是否在禁止执行的时间范围内"""
    # 获取当前时间
    now = datetime.now().time()

    # 定义禁止执行的时间范围
    start_time = dtime(5, 58, 00)  # 05:59:15
    end_time = dtime(7, 31, 00)  # 07:30:30

    # 检查当前时间是否在禁止范围内
    if start_time <= now <= end_time:
        return True
    return False

def read_and_sort_data(file_path: str) -> list:
    """
    读取并预处理彩票数据

    参数：
        file_path: JSON文件路径

    返回：
        按时间排序后的数据列表

    异常：
        FileNotFoundError: 文件不存在时抛出
        ValueError: JSON解析失败时抛出
    """
    try:
        with open(file_path, 'r', encoding='utf-8') as f:
            data = json.load(f)
    except FileNotFoundError:
        raise FileNotFoundError(f"数据文件 {file_path} 未找到")
    except json.JSONDecodeError:
        raise ValueError("JSON文件解析失败，请检查格式")

    return sorted(data['items'], key=lambda x: x['open_time'])

def fetch_and_save_data_one(cookie_value, count_per_page,page, lottery_id,save_dir,TARGET_NUMBERS,BET_CONFIG,COUNTER_DIVISOR,STRATEGY_CONFIG):

    global next_spider_date
    # 循环请求每一页的数据
    url = f'{hosts}/api/mem/lottery/history/{lottery_id}?count={count_per_page}&page={page}&lottery_id={lottery_id}'
    headers = get_headers(cookie_value)
    response = requests.get(url, headers=headers)
    # logger.info(f"{url} 请求头->{headers}")
    output_filename = f"merged_{datetime.now().strftime('%Y%m%d')}.json"
    output_file = os.path.join(save_dir, output_filename)
    # 检查响应状态码
    if response.status_code == 200:
        data = response.json()
        # 生成文件名
        timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
        filename = f"{save_dir}/{timestamp}_lottery_id_{lottery_id}.json"
        # 保存数据到文件
        with open(filename, 'w', encoding='utf-8') as f:
            json.dump(data, f, ensure_ascii=False, indent=4)
        logger.info(f"Saved data to {filename}")
        sorted_items = read_and_sort_data(filename)
        current_date = sorted_items[-1]["open_time"]
        original_date = datetime.strptime(current_date, "%Y-%m-%d %H:%M:%S")
        delta = timedelta(minutes=1, seconds=27)
        next_spider_date = original_date + delta
        merge_json_files(save_dir, output_file)
        calc(output_file,save_dir,TARGET_NUMBERS,BET_CONFIG,COUNTER_DIVISOR,STRATEGY_CONFIG)

        return filename
    else:
        logger.error(f"Request failed with status code: {response.status_code}")
        logger.error(f"Response: {response.text}")
        raise Exception(f"Request failed with status code: {response.status_code}")


def can_afford_bet(current_capital, base_bet_amount, total_bet_numbers):
    """检查当前本金是否足够支付一组投注（总投注金额=单注金额×总押注号码数）"""
    return current_capital >= base_bet_amount * total_bet_numbers

def group_data_by_date(data):
    """按上一天CYCLE_START_HOUR点到下一天CYCLE_END_HOUR点作为一个周期分组数据并按期号升序排列"""
    daily_data = defaultdict(list)
    for item in data['items']:
        open_time = datetime.strptime(item['open_time'], "%Y-%m-%d %H:%M:%S")

        # 计算周期的起始和结束时间点
        if open_time.hour >= int(cai_start1):
            # 开奖时间在起始小时之后，周期起始为当天
            cycle_start_date = open_time.date()
        else:
            # 开奖时间在起始小时之前，周期起始为前一天
            cycle_start_date = open_time.date() - timedelta(days=1)

        # 计算周期起始和结束的完整时间
        cycle_start = datetime.combine(cycle_start_date, datetime.min.time()) + timedelta(hours=int(cai_start1))
        cycle_end = cycle_start + timedelta(days=1)

        # 处理特殊的24点情况
        if int(cai_end1) != 24:
            cycle_end = datetime.combine(cycle_start_date + timedelta(days=1), datetime.min.time()) + timedelta(
                hours=int(cai_end1))

        # 确定当前开奖时间属于哪个周期（以周期结束日期作为周期标识）
        if cycle_start <= open_time < cycle_end:
            cycle_date = cycle_start_date + timedelta(days=1)
        else:
            # 处理边界情况
            if open_time >= cycle_end:
                cycle_date = cycle_start_date + timedelta(days=2)
            else:
                cycle_date = cycle_start_date + timedelta(days=1)

        daily_data[cycle_date].append(item)
    
    
    today_str = str(datetime.now().strftime( "%Y-%m-%d %H:%M:%S"))
    today = datetime.strptime(today_str, "%Y-%m-%d %H:%M:%S")
    next_today = datetime.combine(today + timedelta(days=1), datetime.min.time()) + timedelta(
                hours=int(cai_end1) )
    today_today = datetime.combine(today, datetime.min.time()) + timedelta(
                hours=int(cai_start1))
    if daily_data[next_today.date()]:
        next_data = daily_data[next_today.date()]
        for item in next_data:
             open_time1 = datetime.strptime(item['open_time'], "%Y-%m-%d %H:%M:%S")
             if today_today <= open_time1 < next_today:
                daily_data[today.date()].append(item)

    # 对每天的数据按期号升序排列
    for date in daily_data:
        daily_data[date] = sorted(daily_data[date], key=lambda x: int(x['issue_no']))

    return daily_data


def analyze_lottery(data, target_numbers,bet_config, strategy_config,counter_divisor):
    """分析彩票数据，下注逻辑：当前期中奖个数不为0时，下一期开始新一轮投注，规定3期内中奖则停止本轮"""
    daily_data = group_data_by_date(data)
    daily_results = {}

    # 计算总押注号码数（用于计算投注金额）
    total_bet_numbers = sum(len(codes) for codes in bet_config.values())

    for date, items in daily_data.items():
        date_str = date.strftime("%Y-%m-%d")
        print(f"分析日期: {date_str}，共 {len(items)} 期数据")

        # 初始化不同中奖个数的计数器（1-10个）
        win_count_by_number = {i: 0 for i in range(1, 11)}  # 修改：从1开始统计

        daily_result = {
            'date': date_str,
            'total_issues': len(items),
            'win_count': 0,  # 中奖总次数（每个匹配的号码都算一次）
            'win_count_by_number': win_count_by_number,  # 按中奖个数统计的次数
            'bet_config': bet_config,
            'counter_data': [],
            'consecutive_bet_data': []  # 连续押注数据
        }

        # 从配置中获取参数
        initial_capital = strategy_config["initial_capital"]
        current_capital = initial_capital  # 当前本金
        payout_ratio = strategy_config["payout_ratio"]

        # 当日停止下注标记
        stop_betting_today = False

        # 连续投注跟踪变量
        remaining_bet_periods = 0  # 剩余连续投注期数（0表示不处于连续投注阶段）
        base_bet_amount = strategy_config["base_bet_amount"]  # 每个号码的下注金额
        trigger_next = False  # 标记下一期是否需要开始投注
        bet_round = 0  # 投注轮次
        current_round_won = False  # 当前轮是否已中奖

        # 从第二期开始分析（第一期无历史数据）
        for i in range(1, len(items)):
            current_issue = items[i]
            current_issue_no = current_issue['issue_no']
            current_open_code = current_issue['open_code']
            current_open_time = current_issue['open_time']
            current_codes = current_open_code.split(',')

            # 上一期数据
            prev_issue = items[i - 1]
            prev_issue_no = prev_issue['issue_no']
            prev_open_code = prev_issue['open_code']
            prev_codes = prev_open_code.split(',')

            # 提取上一期关键号码的位置
            positions_target = [
                {"pos": pos + 1, "code": code}
                for pos, code in enumerate(prev_codes)
                if code in target_numbers
            ]

            # 计算当前期中奖情况（支持多号码匹配）
            win_count = 0
            win_positions = []
            win_codes = []
            for target in positions_target:
                pos = target["pos"]
                prev_code = target["code"]
                if 0 <= pos - 1 < len(current_codes):
                    current_code = current_codes[pos - 1]
                    # 检查当前号码是否在押注列表中（支持多个号码）
                    if current_code in bet_config[prev_code]:
                        win_count += 1
                        win_positions.append(pos)
                        win_codes.append(current_code)

            # 统计不同中奖个数的次数（修改：从1开始统计）
            if 1 <= win_count <= 10:
                daily_result['win_count_by_number'][win_count] += 1

            # 更新累计数据
            daily_cumulative_issues = i  # 累计期数（从1开始）
            daily_cumulative_wins = daily_result['win_count'] + win_count  # 累计中奖数

            # 计数器计算（使用公共参数作为除数）
            counter = daily_cumulative_issues / counter_divisor - daily_cumulative_wins
            daily_result['counter_data'].append({
                'issue_no': current_issue_no,
                'daily_cumulative_issues': daily_cumulative_issues,
                'daily_cumulative_wins': daily_cumulative_wins,
                'counter': counter,
                'open_time': current_open_time
            })

            # 更新总中奖数
            daily_result['win_count'] += win_count

            # 下注逻辑
            previous_capital = current_capital  # 记录调整前的本金
            current_bet_amount = 0  # 当期总下注金额（单注金额×总押注号码数）
            current_profit = 0  # 当期盈利
            betting = False  # 是否处于投注状态
            round_ended = False  # 当前轮是否结束
            stopped_by_capital = False  # 是否因本金达到阈值停止下注
            stopped_by_insufficient_funds = False  # 是否因资金不足停止下注

            # 检查是否因本金达到阈值停止下注
            if current_capital > strategy_config["stop_capital_threshold"]:
                stop_betting_today = True
                stopped_by_capital = True

            # 执行连续投注（剩余期数>0时）
            if remaining_bet_periods > 0 and not stop_betting_today:
                # 检查本金是否足够支付当前投注（总金额=单注金额×总押注号码数）
                if can_afford_bet(current_capital, base_bet_amount, total_bet_numbers):
                    betting = True
                    current_bet_amount = base_bet_amount * total_bet_numbers  # 计算总投注金额
                    # 盈利计算公式：中奖个数×单注金额×赔率 - 总投注金额
                    current_profit = win_count * base_bet_amount * payout_ratio - current_bet_amount
                    current_capital += current_profit  # 更新本金

                    # 如果在投注期内中奖，停止本轮投注
                    if win_count > 0:
                        current_round_won = True
                        remaining_bet_periods = 0  # 剩余期数设为0，结束本轮
                        round_ended = True
                    else:
                        remaining_bet_periods -= 1  # 剩余期数减1
                else:
                    # 资金不足，停止当天投注
                    stop_betting_today = True
                    stopped_by_insufficient_funds = True

            # 检查是否需要开始新的连续投注（由上一期触发）
            elif trigger_next and not stop_betting_today:
                # 检查本金是否足够支付一组投注
                if can_afford_bet(current_capital, base_bet_amount, total_bet_numbers):
                    betting = True
                    bet_round += 1  # 开始新的一轮
                    current_round_won = False  # 重置轮次中奖状态

                    current_bet_amount = base_bet_amount * total_bet_numbers  # 计算总投注金额
                    current_profit = win_count * base_bet_amount * payout_ratio - current_bet_amount
                    current_capital += current_profit

                    remaining_bet_periods = strategy_config["consecutive_bet_periods"] - 1  # 剩余期数
                    trigger_next = False  # 重置触发标记

                    # 如果第一轮投注就中奖，立即结束本轮
                    if win_count > 0:
                        current_round_won = True
                        remaining_bet_periods = 0
                        round_ended = True
                else:
                    # 资金不足，停止当天投注
                    stop_betting_today = True
                    stopped_by_insufficient_funds = True
                    trigger_next = False  # 重置触发标记

            # 连续中奖统计
            current_streak = 1 if win_count > 0 else 0
            if daily_result['consecutive_bet_data']:
                last_bet = daily_result['consecutive_bet_data'][-1]
                current_streak = last_bet['current_streak'] + 1 if win_count > 0 else 0
            max_streak = max(
                [bet['current_streak'] for bet in daily_result['consecutive_bet_data']] + [current_streak]
            ) if daily_result['consecutive_bet_data'] else current_streak

            # 格式化下注位置（显示所有押注的号码）
            bet_pos_display = []
            for target in positions_target:
                pos = target["pos"]
                code = target["code"]
                bet_codes = bet_config[code]
                bet_pos_display.append(f"{pos}({'/'.join(bet_codes)})")

            # 记录当期数据
            daily_result['consecutive_bet_data'].append({
                'prev_issue_no': prev_issue_no,
                'current_issue_no': current_issue_no,
                'prev_open_code': prev_open_code,
                'bet_positions': ", ".join(bet_pos_display),
                'current_open_code': current_open_code,
                'win_count': win_count,
                'win_positions': win_positions,
                'win_codes': win_codes,
                'current_streak': current_streak,
                'max_streak': max_streak,
                'open_time': current_open_time,
                'daily_cumulative_issues': daily_cumulative_issues,
                'daily_cumulative_wins': daily_cumulative_wins,
                'counter': counter,
                'betting': betting,  # 是否在投注期
                'base_bet_amount': base_bet_amount,
                'total_bet_numbers': total_bet_numbers,  # 总押注号码数
                'bet_amount': current_bet_amount,
                'profit': current_profit,
                'previous_capital': previous_capital,
                'current_capital': current_capital,
                'remaining_bet_periods': remaining_bet_periods,
                'trigger_next': trigger_next,
                'win_in_current': win_count > 0,
                'bet_round': bet_round,
                'current_round_won': current_round_won,
                'round_ended': round_ended,
                'stop_betting_today': stop_betting_today,
                'stopped_by_capital': stopped_by_capital,
                'stopped_by_insufficient_funds': stopped_by_insufficient_funds
            })

            # 核心逻辑：当前期中奖个数不为0时，下一期开始新一轮投注
            if win_count > 0 and remaining_bet_periods == 0 and not stop_betting_today:
                # 检查本金是否足够支付下一组投注
                if can_afford_bet(current_capital, base_bet_amount, total_bet_numbers):
                    trigger_next = True  # 当前期中奖且不在投注期，下一期开始投注
                else:
                    trigger_next = False  # 资金不足，不触发投注
            else:
                trigger_next = False

        # 保存最大连胜次数
        daily_result['max_streak'] = max(
            [bet['max_streak'] for bet in daily_result['consecutive_bet_data']]
        ) if daily_result['consecutive_bet_data'] else 0

        # 计算当天总盈利
        if daily_result['consecutive_bet_data']:
            daily_result['daily_profit'] = daily_result['consecutive_bet_data'][-1]['current_capital'] - initial_capital
        else:
            daily_result['daily_profit'] = 0

        daily_results[date] = daily_result

    return daily_results


def generate_counter_chart(counter_data, output_dir, date_str, counter_divisor):
    """生成计数器折线图"""
    if not counter_data:
        return None

    sorted_counter_data = sorted(
        counter_data,
        key=lambda x: int(x['issue_no'])
    )

    issue_nos = [item['issue_no'] for item in sorted_counter_data]
    counters = [item['counter'] for item in sorted_counter_data]

    tooltip_data = [
        f"期号: {item['issue_no']}<br/>开奖时间: {item['open_time']}<br/>计数器: {item['counter']:.2f}"
        for item in sorted_counter_data
    ]

    line = (
        Line(init_opts=opts.InitOpts(
            theme=ThemeType.LIGHT,
            width="1000px",
            height="500px"
        ))
        .add_xaxis(issue_nos)
        .add_yaxis(
            "计数器",
            counters,
            is_smooth=True,
            label_opts=opts.LabelOpts(is_show=False),
            itemstyle_opts=opts.ItemStyleOpts(color="#3398DB")
        )
        .set_global_opts(
            title_opts=opts.TitleOpts(title=f"{date_str} 计数器变化趋势"),
            xaxis_opts=opts.AxisOpts(
                name="期号",
                axislabel_opts=opts.LabelOpts(rotate=60)
            ),
            yaxis_opts=opts.AxisOpts(
                name="计数器值",
                splitline_opts=opts.SplitLineOpts(is_show=True)
            ),
            toolbox_opts=opts.ToolboxOpts(is_show=True),
            tooltip_opts=opts.TooltipOpts(
                trigger="axis",
                formatter=lambda params: tooltip_data[params[0].data_index]
            ),
            datazoom_opts=[opts.DataZoomOpts()]
        )
    )

    chart_file = os.path.join(output_dir, f"counter_chart_{date_str}.html")
    line.render(chart_file)
    return chart_file


def generate_html_report(daily_result,data,output_dir, target_numbers,bet_config,strategy_config,
                         counter_divisor):
    """生成每日分析报告HTML"""
    date_str = daily_result['date']
    # 计算实际分析的日期范围
    report_date = datetime.strptime(date_str, "%Y-%m-%d").date()
    start_date = report_date - timedelta(days=1)
    date_range_str = f"{start_date.strftime('%Y-%m-%d')} {int(cai_start1)}:00:00 至 {date_str} {int(cai_end1)}:00:00"

    output_file = os.path.join(output_dir, f"lottery_analysis_{date_str}.html")

    # 基础统计
    total_issues = daily_result['total_issues'] - 1  # 总投注期数
    total_wins = daily_result['win_count']
    win_rate = f"{(total_wins / total_issues * 100) if total_issues > 0 else 0:.2f}%"

    # 配置显示
    config_display = ", ".join([f"{code}→{'/'.join(codes)}" for code, codes in daily_result['bet_config'].items()])
    total_bet_numbers = sum(len(codes) for codes in daily_result['bet_config'].values())  # 总押注号码数

    # 计数器图表
    counter_chart = generate_counter_chart(daily_result['counter_data'], output_dir, date_str, counter_divisor)
    chart_iframe = f'<iframe src="{os.path.basename(counter_chart)}" width="100%" height="550" frameborder="0"></iframe>' if (
            counter_chart and os.path.exists(counter_chart)) else '<div>无数据生成图表</div>'

    # 盈利统计
    consecutive_data = daily_result['consecutive_bet_data']
    final_capital = consecutive_data[-1]['current_capital'] if consecutive_data else strategy_config["initial_capital"]
    total_profit = final_capital - strategy_config["initial_capital"]

    # 新增：计算当天总押注金额
    total_bet_amount = sum(bet['bet_amount'] for bet in consecutive_data if bet['betting'])

    # 新增：计算纯毛利（纯毛利=总盈利+总押注金额×0.028）
    pure_profit = total_profit + total_bet_amount * 0.028

    # 连续投注表格
    bet_table_html = ""
    for bet in consecutive_data:
        bet_positions = bet['bet_positions']
        win_positions = ", ".join(map(str, bet['win_positions'])) if bet['win_positions'] else "无中奖"
        win_codes = ", ".join(bet['win_codes']) if bet['win_codes'] else ""
        row_class = ' class="win"' if bet['win_count'] > 0 else ''
        if bet['betting']:
            row_class += ' betting'  # 投注期高亮
        if bet['stopped_by_capital']:
            row_class += ' stopped-by-capital'
        if bet['stopped_by_insufficient_funds']:
            row_class += ' stopped-by-insufficient-funds'

        # 盈利显示格式化
        profit_display = f"{bet['profit']:.2f}"
        if bet['profit'] > 0:
            profit_display = f"<span style='color:green'>+{profit_display}</span>"
        elif bet['profit'] < 0:
            profit_display = f"<span style='color:red'>{profit_display}</span>"

        # 标记触发投注和轮次结束的行
        trigger_class = ' class="trigger-bet"' if bet['trigger_next'] else ''
        round_end_class = ' class="round-end"' if bet['round_ended'] else ''

        bet_table_html += f"""
            <tr{row_class}{trigger_class}{round_end_class}>
                <td>{bet['prev_issue_no']}</td>
                <td>{bet['prev_open_code']}</td>
                <td>{bet_positions}</td>
                <td>{bet['current_issue_no']}</td>
                <td>{bet['current_open_code']}</td>
                <td>{win_positions}</td>
                <td>{win_codes}</td>
                <td>{bet['win_count']}</td>
                <td>{bet['current_streak']}</td>
                <td>{bet['daily_cumulative_issues']}</td>
                <td>{bet['daily_cumulative_wins']}</td>
                <td style="font-weight: bold;">{bet['counter']:.2f}</td>
                <td>{bet['open_time']}</td>
                <td>{'是' if bet['betting'] else '否'}</td>
                <td>{bet['base_bet_amount']}元/号</td>
                <td>{bet['total_bet_numbers']}个号码</td>
                <td>{bet['bet_amount'] if bet['betting'] else '-'}</td>
                <td>{profit_display}</td>
                <td style="font-weight: bold;">{bet['current_capital']:.2f}</td>
                <td>{bet['remaining_bet_periods']}</td>
                <td>{'是' if bet['trigger_next'] else '否'}</td>
                <td>{'是' if bet['win_in_current'] else '否'}</td>
                <td>{bet['bet_round']}</td>
                <td>{'是' if bet['current_round_won'] else '否'}</td>
                <td>{'是' if bet['round_ended'] else '否'}</td>
                <td>{'是' if bet['stop_betting_today'] else '否'}</td>
                <td>{'是' if bet['stopped_by_insufficient_funds'] else '否'}</td>
            </tr>
        """

    # HTML内容
    html_content = f"""
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="refresh" content="45">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{date_str} 彩票下注分析报告（分析周期：{date_range_str}）</title>
    <style>
        body {{ font-family: Arial, sans-serif; margin: 20px; }}
        h1 {{ color: #333; text-align: center; }}
        .stats-box, .config-box {{ padding: 15px; border-radius: 5px; margin-bottom: 20px; }}
        .stats-box {{ background-color: #f0f0f0; }}
        .config-box {{ background-color: #e6f7ff; }}
        .chart-box {{ margin: 30px 0; padding: 15px; background-color: #f8f9fa; border-radius: 5px; }}
        .table-container {{ overflow-x: auto; margin: 30px 0; }}
        table {{ width: 100%; border-collapse: collapse; }}
        th, td {{ border: 1px solid #ddd; padding: 8px; text-align: left; }}
        th {{ background-color: #e6e6e6; }}
        tr:nth-child(even) {{ background-color: #f9f9f9; }}
        .win {{ background-color: #d4edda; }}
        .betting {{ outline: 2px solid #3b82f6; }}
        .trigger-bet {{ outline: 2px solid #f59e0b; }}
        .round-end {{ outline: 2px solid #ef4444; }}
        .stopped-by-capital {{ outline: 2px solid #8b5cf6; }}
        .stopped-by-insufficient-funds {{ outline: 2px solid #10b981; }}
        .nav {{ margin-bottom: 20px; text-align: center; }}
        .nav a {{ margin: 0 10px; text-decoration: none; color: #0066cc; }}
        .profit-summary {{ margin: 20px 0; padding: 15px; background-color: #e6f7ff; border-radius: 5px; }}
        .win-count-grid {{ display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 10px; margin-top: 10px; }}
        .win-count-item {{ padding: 5px; background-color: #f9f9f9; border-radius: 3px; }}
    </style>
</head>
<body>
    <div class="nav">
        <a href="index.html">返回总览</a>
        <a href="summary.html">查看汇总报告</a>
    </div>
    <h1>{date_str} 彩票下注分析报告（分析周期：{date_range_str}）</h1>

    <div class="config-box">
        <h3>下注规则配置</h3>
        <div>规则: {config_display}</div>
        <div>初始本金: {strategy_config["initial_capital"]}元</div>
        <div>连续投注期数上限: {strategy_config["consecutive_bet_periods"]}期</div>
        <div>单注金额(每个号码): {strategy_config["base_bet_amount"]}元</div>
        <div>总押注号码数: {total_bet_numbers}个</div>
        <div>每组投注金额: {strategy_config["base_bet_amount"]} × {total_bet_numbers} = {strategy_config["base_bet_amount"] * total_bet_numbers}元</div>
        <div>赔率: {strategy_config["payout_ratio"]}</div>
        <div>计数器除数: {counter_divisor}</div>
        <div>停止下注阈值: 本金 > {strategy_config["stop_capital_threshold"]}元</div>
    </div>

    <div class="stats-box">
        <h3>当日统计数据（分析周期：{date_range_str}）</h3>
        <div>总期数: {daily_result['total_issues']}</div>
        <div>投注期数: {total_issues}</div>
        <div>中奖总次数: {total_wins}</div>
        <div>整体中奖率: {win_rate}</div>
        <div>最大连胜次数: {daily_result['max_streak']} 次</div>

        <h4 style="margin-top: 10px; margin-bottom: 5px;">按中奖个数统计次数：</h4>
        <div class="win-count-grid">
            {''.join([f'<div class="win-count-item">中奖个数={k}：{v} 次</div>' for k, v in daily_result['win_count_by_number'].items()])}
        </div>
    </div>

    <div class="profit-summary">
        <h3>盈利统计</h3>
        <div>初始本金: {strategy_config["initial_capital"]:.2f}</div>
        <div>最终本金: {final_capital:.2f}</div>
        <div>总盈利: <span style="color:{'green' if total_profit > 0 else 'red'}">{total_profit:+.2f}</span></div>
        <div>总押注金额: {total_bet_amount:.2f} 元</div>
        <div>纯毛利: <span style="color:{'green' if pure_profit > 0 else 'red'}">{pure_profit:+.2f}</span> 元</div>
        <div>（纯毛利公式：纯毛利=总盈利+总押注金额×0.028）</div>
    </div>

    <div class="chart-box">
        <h3>计数器变化趋势</h3>
        <p>计数器 = 累计总期数 / {counter_divisor} - 累计中奖个数</p>
        {chart_iframe}
    </div>

    <div class="table-container">
        <h3>每日投注中奖情况</h3>
        <p>下注策略：初始本金{strategy_config["initial_capital"]}元，<strong>当前期中奖个数不为0时，下一期开始投注；若在接下来的{strategy_config["consecutive_bet_periods"]}期内中奖，则立即停止投注；当剩余本金>{strategy_config["stop_capital_threshold"]}时，当天停止下注；若剩余本金不足单组金额（{strategy_config["base_bet_amount"]}元×{total_bet_numbers}={strategy_config["base_bet_amount"] * total_bet_numbers}元），也停止下注</strong></p>
        <p>盈利公式：当期盈利 = 中奖个数 × {strategy_config["base_bet_amount"]} × {strategy_config["payout_ratio"]} - {strategy_config["base_bet_amount"]} × {total_bet_numbers}</p>
        <p>表格说明：</p>
        <ul>
            <li>绿色背景：当期有中奖</li>
            <li>蓝色边框：当期处于投注期</li>
            <li>橙色边框：当期触发下一期开始投注（当前期中奖但未投注）</li>
            <li>红色边框：当前轮次结束（可能是因为中奖或达到{strategy_config["consecutive_bet_periods"]}期）</li>
            <li>紫色边框：因本金>{strategy_config["stop_capital_threshold"]}停止下注的期</li>
            <li>青绿色边框：因本金不足单组金额停止下注的期</li>
        </ul>

        <table>
            <tr>
                <th>上一期期号</th>
                <th>上一期开奖号码</th>
                <th>下注位置(位置+号码)</th>
                <th>当前期期号</th>
                <th>当前期开奖号码</th>
                <th>中奖位置</th>
                <th>中奖号码</th>
                <th>中奖个数</th>
                <th>当前连胜</th>
                <th>累计期数</th>
                <th>累计中奖</th>
                <th>计数器</th>
                <th>开奖时间</th>
                <th>是否投注</th>
                <th>单注金额</th>
                <th>总押注号码</th>
                <th>总下注</th>
                <th>当期盈利</th>
                <th>当前本金</th>
                <th>剩余投注期数</th>
                <th>触发下一期</th>
                <th>当前期是否中奖</th>
                <th>投注轮次</th>
                <th>轮次是否已中奖</th>
                <th>轮次是否结束</th>
                <th>停止下注</th>
                <th>资金不足</th>
            </tr>
            {bet_table_html}
        </table>
    </div>
</body>
</html>
    """

    with open(output_file, 'w', encoding='utf-8') as f:
        f.write(html_content)
    return output_file


def generate_index_html(dates, output_dir):
    """生成索引页面"""
    output_file = os.path.join(output_dir, "index.html")
    html_content = """
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>彩票下注分析报告总览</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 20px; }
        h1 { color: #333; text-align: center; }
        .date-list { max-width: 800px; margin: 0 auto; }
        .date-item { margin: 10px 0; padding: 10px; background-color: #f0f0f0; border-radius: 5px; }
        .date-item a { text-decoration: none; color: #0066cc; }
        .summary-link { text-align: center; margin: 20px 0; }
    </style>
</head>
<body>
    <div class="summary-link">
        <a href="summary.html">查看汇总报告</a>
    </div>
    <h1>彩票下注分析报告总览</h1>
    <div class="date-list">
    """

    for date in sorted(dates):
        date_str = date.strftime("%Y-%m-%d")
        html_content += f"""
        <div class="date-item">
            <a href="lottery_analysis_{date_str}.html">{date_str}</a>
        </div>
        """

    html_content += """
    </div>
</body>
</html>
    """

    with open(output_file, 'w', encoding='utf-8') as f:
        f.write(html_content)
    return output_file


def generate_summary_html(daily_results, output_dir, strategy_config, counter_divisor):
    """生成汇总界面"""
    output_file = os.path.join(output_dir, "summary.html")

    # 按月统计（过滤投注期数≥1070的天）
    monthly_data = defaultdict(list)
    valid_daily_results = []

    for date, result in daily_results.items():
        total_issues = result['total_issues'] - 1
        if total_issues >= 1070:
            year_month = f"{date.year}-{date.month:02d}"
            monthly_data[year_month].append(result)
            valid_daily_results.append(result)

    # 每月平均中奖次数
    monthly_avg_win_counts = {}
    for year_month, results in monthly_data.items():
        total_win_count = sum(result['win_count'] for result in results)
        monthly_avg_win_counts[year_month] = {
            'days': len(results),
            'avg_win_count': total_win_count / len(results)
        }

    # 总平均中奖次数
    total_avg_win_count = sum(result['win_count'] for result in valid_daily_results) / len(
        valid_daily_results) if valid_daily_results else 0

    # 最大计数器平均值
    total_max_counter = 0
    valid_counter_days = 0
    for result in valid_daily_results:
        if result['counter_data']:
            total_max_counter += max(item['counter'] for item in result['counter_data'])
            valid_counter_days += 1
    avg_max_counter = total_max_counter / valid_counter_days if valid_counter_days else 0

    # 按月统计每天总盈利
    monthly_profit_data = defaultdict(list)
    for date, result in daily_results.items():
        year_month = f"{date.year}-{date.month:02d}"
        daily_profit = result.get('daily_profit', 0)
        monthly_profit_data[year_month].append({
            'date': date.strftime("%Y-%m-%d"),
            'profit': daily_profit
        })

    # 计算每月总盈利和平均每日盈利
    monthly_profit_summary = {}
    for year_month, profit_data in monthly_profit_data.items():
        total_profit = sum(item['profit'] for item in profit_data)
        avg_daily_profit = total_profit / len(profit_data)
        monthly_profit_summary[year_month] = {
            'total_profit': total_profit,
            'avg_daily_profit': avg_daily_profit,
            'days': len(profit_data),
            'profit_data': profit_data
        }

    # HTML内容
    html_content = f"""
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>彩票下注分析汇总报告</title>
    <style>
        body {{ font-family: Arial, sans-serif; margin: 20px; }}
        h1 {{ color: #333; text-align: center; }}
        .section {{ margin: 30px 0; padding: 15px; background-color: #f8f9fa; border-radius: 5px; }}
        .stats-box {{ background-color: #f0f0f0; padding: 15px; margin-bottom: 20px; }}
        .table-container {{ overflow-x: auto; margin: 30px 0; }}
        table {{ width: 100%; border-collapse: collapse; }}
        th, td {{ border: 1px solid #ddd; padding: 8px; }}
        th {{ background-color: #e6e6e6; }}
        .profit-positive {{ color: green; }}
        .profit-negative {{ color: red; }}
        .below-threshold {{ background-color: #f5f5f5; }}
        .nav {{ margin-bottom: 20px; text-align: center; }}
        .nav a {{ margin: 0 10px; text-decoration: none; color: #0066cc; }}
        .avg-stats {{ background-color: #e6f7ff; padding: 10px; margin-bottom: 15px; border-radius: 5px; }}
        .month-header {{ background-color: #e6f7ff; padding: 10px; margin: 10px 0; border-radius: 5px; }}
        .monthly-profit-summary {{ background-color: #d1e7dd; padding: 15px; margin-bottom: 20px; border-radius: 5px; }}
        .monthly-profit-table {{ margin-top: 10px; }}
        .monthly-profit-table th, .monthly-profit-table td {{ border: 1px solid #ddd; padding: 8px; text-align: center; }}
        .monthly-profit-table th {{ background-color: #c1d6e1; }}
        .monthly-profit-total {{ font-weight: bold; }}
    </style>
</head>
<body>
    <div class="nav">
        <a href="index.html">返回日期列表</a>
    </div>
    <h1>彩票下注分析汇总报告</h1>

    <div class="stats-box">
        <h3>下注规则说明</h3>
        <div>计数器计算公式：计数器 = 累计总期数 / {counter_divisor} - 累计中奖个数</div>
        <div>初始本金{strategy_config["initial_capital"]}元，<strong>当前期中奖个数不为0时，下一期开始投注；若在接下来的{strategy_config["consecutive_bet_periods"]}期内中奖，则立即停止投注；当剩余本金>{strategy_config["stop_capital_threshold"]}时，当天停止下注；若剩余本金不足单组金额（单注金额×总押注号码数），也停止下注</strong></div>
        <div>盈利公式：当期盈利 = 中奖个数 × {strategy_config["base_bet_amount"]} × {strategy_config["payout_ratio"]} - {strategy_config["base_bet_amount"]} × 总押注号码数</div>
    </div>

    <div class="section monthly-profit-summary">
        <h2>按月份统计每日总盈利</h2>
        <div class="avg-stats">
            <p>统计了 <strong>{len(daily_results)}</strong> 天的数据</p>
        </div>
    """

    # 生成每月盈利汇总表格
    for year_month in sorted(monthly_profit_summary.keys()):
        month_data = monthly_profit_summary[year_month]
        total_profit = month_data['total_profit']
        avg_daily_profit = month_data['avg_daily_profit']
        days = month_data['days']

        total_profit_display = f"{total_profit:+.2f}"
        total_profit_class = "profit-positive" if total_profit >= 0 else "profit-negative"

        avg_daily_profit_display = f"{avg_daily_profit:+.2f}"
        avg_daily_profit_class = "profit-positive" if avg_daily_profit >= 0 else "profit-negative"

        daily_profit_rows = ""
        for item in sorted(month_data['profit_data'], key=lambda x: x['date']):
            profit = item['profit']
            profit_display = f"{profit:+.2f}"
            profit_class = "profit-positive" if profit >= 0 else "profit-negative"
            daily_profit_rows += f"""
                <tr>
                    <td>{item['date']}</td>
                    <td class="{profit_class}">{profit_display}</td>
                </tr>
            """

        html_content += f"""
        <div class="month-header">
            <h3>{year_month}（有效天数：{days}）</h3>
            <p>月总盈利：<span class="{total_profit_class}"><strong>{total_profit_display}</strong></span> 元</p>
            <p>平均每日盈利：<span class="{avg_daily_profit_class}"><strong>{avg_daily_profit_display}</strong></span> 元</p>
        </div>
        <div class="monthly-profit-table">
            <table>
                <tr>
                    <th>日期</th>
                    <th>当日总盈利（元）</th>
                </tr>
                {daily_profit_rows}
            </table>
        </div>
        """

    html_content += """
    </div>

    <div class="section">
        <h2>按月统计中奖次数平均值</h2>
        <p>统计投注期数≥1070的天，每月平均中奖次数</p>
        <div class="avg-stats">
            <p>有效天数共 <strong>""" + str(
        len(valid_daily_results)) + """</strong> 天，总平均每天中奖 <strong>""" + f"{total_avg_win_count:.2f}" + """</strong> 次</p>
        </div>
    """

    for year_month in sorted(monthly_avg_win_counts.keys()):
        data = monthly_avg_win_counts[year_month]
        html_content += f"""
            <div class="month-header">
                <h3>{year_month}（有效天数：{data['days']}）</h3>
                <p>平均每天中奖：<strong>{data['avg_win_count']:.2f}</strong> 次</p>
            </div>
        """

    html_content += """
        </div>
    </div>

    <div class="section">
        <h2>每天中奖个数统计</h2>
        <div class="avg-stats">
            <p>有效天数共 <strong>""" + str(
        len(valid_daily_results)) + """</strong> 天，平均每天中奖 <strong>""" + f"{total_avg_win_count:.2f}" + """</strong> 次</p>
        </div>
    </div>

    <div class="section">
        <h2>每日最大计数器平均值</h2>
        <p>统计投注期数≥1070的天，每日最大计数器的平均值</p>
        <div class="avg-stats">
            <p>有效天数共 <strong>""" + str(
        valid_counter_days) + """</strong> 天，平均每日最大计数器值为 <strong>""" + f"{avg_max_counter:.2f}" + """</strong></p>
        </div>
    </div>
</body>
</html>
    """

    with open(output_file, 'w', encoding='utf-8') as f:
        f.write(html_content)
    return output_file
def spiderCharData1(cookie_value,lottery_id,ip,host,choose_number,jsq,initial_capital,base_bet_amount,stop_capital_threshold,cai_start,cai_end):
    try:
        global ips,hosts,next_spider_date,logger,fetch_data,logger,jsq1,COUNTER_DIVISOR,STRATEGY_CONFIG,cai_start1,cai_end1
        logger = setup_logging(
        name="mychar",
        log_dir="./runtime/logs",
        level="DEBUG")
        next_spider_date = None
        cai_start1 = cai_start
        cai_end1 = cai_end
        # 1. 按逗号分割成键值对列表
        pairs = choose_number.split(',')
        BET_CONFIG = {}
        for pair in pairs:
            key, value = pair.split(':')
            if key in BET_CONFIG:
                BET_CONFIG[key].append(value)
            else:
                BET_CONFIG[key] = [value]
        TARGET_NUMBERS = list(BET_CONFIG.keys())

        jsq1 = float(jsq)
        COUNTER_DIVISOR = float(jsq)
        STRATEGY_CONFIG = {
        "initial_capital": int(initial_capital),  # 初始本金
         "consecutive_bet_periods": 50,  # 连续投注期数上限
         "base_bet_amount": 2,  # 单注金额（每个号码的投注金额）
         "payout_ratio": 9.7052,  # 赔率
         "stop_capital_threshold": int(stop_capital_threshold) # 停止下注的本金阈值
         }

        ips = ip
        hosts = host
        fetch_data = False
        total_records = calculate_event_count()+1200
        count_per_page1 = 50
        current_date_1 = datetime.now().strftime('%Y%m%d')
        save_dir = f"spider_char_car_data_{current_date_1}"
        clear_directory(save_dir)
        count_per_page = 2  # 每页记录数
        page = 0
        if next_spider_date == None:
            fetch_and_save_data(cookie_value, total_records, count_per_page1, lottery_id,save_dir,TARGET_NUMBERS,BET_CONFIG,COUNTER_DIVISOR,STRATEGY_CONFIG)
        while True:
            # 获取当前日期时间
            current_date = datetime.now()  # 本地计算机时间
            # 判断是否是休息时间
            if should_execute():
                logger.info('当前是禁止时间段，不执行操作逻辑,获取分析图')
                time.sleep(10)  # 适当延迟
                continue
            # 下次爬取时间为空或者当前时间大于等于下次获取时间。则需要重新获取数据
            if not fetch_data and (next_spider_date is None or current_date >= next_spider_date):
                fetch_data = True
                logger.info(f"获取分析图-》获取新一轮的数据:{next_spider_date}")
                # 获取数据保存起来
                fetch_and_save_data_one(cookie_value, count_per_page,page,lottery_id,save_dir,TARGET_NUMBERS,BET_CONFIG,COUNTER_DIVISOR,STRATEGY_CONFIG)
                fetch_data = False
            time.sleep(1)
    except Exception as e:
        logger.error(f"hu - {str(e)}")
        return False

def calc(json_file,save_dir,TARGET_NUMBERS,BET_CONFIG,COUNTER_DIVISOR,STRATEGY_CONFIG):
    """主函数"""
    today = datetime.now().strftime("%Y-%m-%d")
    output_dir = f"{save_dir}//lottery_reports//car4"
    os.makedirs(output_dir, exist_ok=True)
    try:
        with open(json_file, 'r', encoding='utf-8') as f:
            data = json.load(f)
          # 分析数据
        # 过滤出当天数据
        filtered_items = [item for item in data.get('items', [])]
        seen_issues = {}
        unique_items = []
        # 先按open_time降序排序，确保每个issue_no只保留最新的一条
        for item in sorted(filtered_items, key=lambda x: x.get('open_time', ''), reverse=True):
            issue_no = item.get('issue_no')
            if issue_no and issue_no not in seen_issues:
                seen_issues[issue_no] = True
                unique_items.append(item)
        data = {"items": sorted(unique_items, key=lambda x: x.get('open_time', ''))}
        daily_results = analyze_lottery(data,TARGET_NUMBERS,BET_CONFIG,STRATEGY_CONFIG,COUNTER_DIVISOR)
        if not daily_results:
            print("警告: 没有找到有效的数据进行分析")
            return

        print("正在生成每日报告...")
        for date, result in daily_results.items():
            date_str = date.strftime("%Y-%m-%d")
            if today != date_str:
                continue
            html_content = generate_html_report(result, data, output_dir,TARGET_NUMBERS,BET_CONFIG,STRATEGY_CONFIG,COUNTER_DIVISOR)

        index_file = generate_index_html(daily_results.keys(), output_dir)
        print(f"已生成索引页面: {index_file}")

        # 新增：生成汇总页面
        summary_file = generate_summary_html(daily_results, output_dir,STRATEGY_CONFIG,COUNTER_DIVISOR)
        print(f"已生成汇总页面: {summary_file}")

    except Exception as e:
        print(f"处理过程中出错: {e}")

if __name__ == "__main__":
    cookie = "visid_incap_2980419=2z0bZ1ioTDGEX51K1e8N0Mdzk2gAAAAAQUIPAAAAAADy+lklBlr3J1aK9U/tZwLt; incap_ses_1510_2980419=Akg9Go6x/xC3HXLRMJn0FMdzk2gAAAAAutaap5LlrvrNmSX9q/gyLg==; koa:sess=be3071d993a8e9531e34a9cf5286e3ba014e7461325c1bec; nlbi_2980419=3GmAQn+F3xgZ4EgphHMyXgAAAADmSei2IMJY9z44XwK47/mz"
    spiderCharData1(cookie,12,"c181532.sys998.com","https://c181532.sys998.com","01:01,01:06,02:02,02:07,03:03,03:08,04:04,04:09","2.5","100","100","100",'9','9')